package com.work05.securityconfig;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.ProviderManager;
import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.provisioning.JdbcUserDetailsManager;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.access.AccessDeniedHandler;

import javax.annotation.Resource;
import javax.servlet.RequestDispatcher;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.sql.DataSource;
import java.io.IOException;
import java.util.List;

import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.web.authentication.rememberme.JdbcTokenRepositoryImpl;

/**
 * @author 绛河
 * @date 2023/5/25
 * @apiNote
 */
@Configuration
public class JdbcSecurityConfig {
    @Resource
    private DataSource dataSource;

    @Bean
    SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
        // 自定义用户授权管理
        http.authorizeRequests()
                .antMatchers("/", "/goods", "/toLoginPage").permitAll()
                .antMatchers("/login/**", "/css/**", "/js/**").permitAll()
                //不同请求设置不同角色
                .antMatchers("/purchaser/buy").hasRole("purchaser")
                .antMatchers("/admin/goods/check", "/admin/shop/check").hasRole("admin")
                .antMatchers("/shopkeeper/publish").hasRole("shopkeeper")
                .anyRequest().authenticated();//匹配已认证的用户

        // 自定义用户登录控制
        http.formLogin()
                .loginPage("/login").permitAll()
                .usernameParameter("name").passwordParameter("pwd")
                .defaultSuccessUrl("/goods")
                .failureUrl("/login?error");

        // 针对访问无权限页面出现的403页面进行定制处理
        http.exceptionHandling().accessDeniedHandler(new AccessDeniedHandler() {
            @Override
            public void handle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, AccessDeniedException e) throws IOException, ServletException {
                // 如果是权限访问异常，则进行拦截到指定错误页面
                RequestDispatcher dispatcher = httpServletRequest.getRequestDispatcher("/errorPage/error_403");
                dispatcher.forward(httpServletRequest, httpServletResponse);
            }
        });


        return http.build();
    }


    /**
     * 持久化Token存储
     *
     * @return
     */
    @Bean
    public JdbcTokenRepositoryImpl tokenRepository() {
        JdbcTokenRepositoryImpl jr = new JdbcTokenRepositoryImpl();
        jr.setDataSource(dataSource);
        jr.setCreateTableOnStartup(false);
        return jr;
    }


    @Bean
    public AuthenticationManager authenticationManager(HttpSecurity httpSecurity) throws Exception {
        // 1、使用JDBC进行身份认证
        String userSQL = "select account,password,role_id from account where account = ?";
        String authoritySQL = "select a.account,r.role_code from account a,role r where a.role_id = r.id and a.account = ?";
        AuthenticationManager authenticationManager = httpSecurity.getSharedObject(AuthenticationManagerBuilder.class)
                .jdbcAuthentication()
                .passwordEncoder(passwordEncoder())
                .dataSource(dataSource)
                .usersByUsernameQuery(userSQL)
                .authoritiesByUsernameQuery(authoritySQL).and().build();
        return authenticationManager;


        //UserDetailsServiceImpl身份认证
        //AuthenticationManager authenticationManager = httpSecurity.getSharedObject(AuthenticationManagerBuilder.class)
        //        .userDetailsService(userDetailsService)
        //        .passwordEncoder(passwordEncoder())
        //        .and()
        //        .build();
        //return authenticationManager;
    }

    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }
}
